
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

#include <ez_util.h>
#include <ez_net.h>
#include <ez_log.h>
#include <ez_event.h>
// --------------------------------------------------------------------
// 读：一直读，如果读返回0,中止读，删除事件，close socket。
//           如果读返回-1,如果 ERRNO = EAGAIN，下次可读事件来时继续读。否则中止读。

// 写：一直写，如果写返回0，继续写，加入写事件，下次可写事件来时继续写。
//           如果写返回-1,如果 ERRNO = EAGAIN，下次可写事件来时继续写。否则中止写。
// --------------------------------------------------------------------
int time_out_handler(ezEventLoop * eventLoop, int64_t timeId, void *clientData)
{
	EZ_NOTUSED(eventLoop);
	EZ_NOTUSED(clientData);
	log_info("==> timeId %li !", timeId);
	if (timeId != 0)
		return AE_TIMER_END;
	return AE_TIMER_NEXT;
}

void read_char_from_stdin(ezEventLoop * eventLoop, int fd, void *clientData, int mask)
{
	EZ_NOTUSED(clientData);
	EZ_NOTUSED(mask);

	char buf[33];
	ssize_t nread = read(fd, buf, 32);
	if (nread > 0) {
		buf[nread] = '\0';
		if (buf[0] == 'q' || buf[0] == 'Q') {
			log_info("good bye!");
			ez_delete_file_event(eventLoop, fd, AE_READABLE);
			ez_stop(eventLoop);
		}
	}
}

void read_client_handler(ezEventLoop * eventLoop, int c, void *clientData, int mask)
{
#define buf_size 32
	char buf[buf_size + 1];
	int  r = 0;
	ssize_t nread = 0;

	EZ_NOTUSED(clientData);
	EZ_NOTUSED(mask);

	r = ez_net_read(c, buf, buf_size, &nread);
	if (r == ANET_ERR) {
		log_info("read client %d error:[%d,%s]!", c, errno, strerror(errno));
		ez_delete_file_event(eventLoop, c, AE_READABLE);
		close(c);
		return;

	} else if (r == ANET_EAGAIN) {
		return;

	} else if (r == ANET_OK && nread == 0) {
		// ez_event 会在 client 端close也引发 AE_READABLE 事件，这时读取时就会返回 0
		// 而epoll又通知有 AE_READABLE 事件，就是表示 对端已经关闭了这个socket.
		log_info("client %d closed connection !", c);
		ez_delete_file_event(eventLoop, c, AE_READABLE);
		close(c);
		return;
	}
	if (nread) {
		buf[nread] = '\0';
		log_hexdump(LOG_INFO, buf, nread, "read client id:%d read %d bytes!", c, nread);
	}
}

void accept_handler(ezEventLoop * eventLoop, int s, void *clientData, int mask)
{
	EZ_NOTUSED(clientData);
	EZ_NOTUSED(mask);

	int c = ez_net_tcp_accept(s);
	if (c == ANET_ERR) {
		log_info("server accept error [%d,%s]", errno, strerror(errno));
		return;
	} else {
		log_info("new client:%d connect.", c);
		ez_net_tcp_enable_nodelay(c);
		ez_net_tcp_keepalive(c, 30);

		if (ez_create_file_event(eventLoop, c, AE_READABLE, read_client_handler, NULL) == AE_ERR) {
			log_info("add eventloop new client %d AE_READABLE failed!", c);
			close(c);
		}
	}
}

int main(int argc, char **argv)
{
	int port = 9090;
	EZ_NOTUSED(argc);
	EZ_NOTUSED(argv);

	signal(SIGHUP, SIG_IGN);
	signal(SIGPIPE, SIG_IGN);

	log_init(LOG_VVERB, NULL);

	int s = ez_net_tcp_server(port, NULL, 1024);
	int s6 = ez_net_tcp6_server(port, NULL, 1024);

	log_info("server %d at port %d wait client ...", s, port);
	log_info("server %d at port %d wait client ...", s6, port);

	log_info("create event loop...");
	ezEventLoop *main_event = ez_create_event_loop(1024);

	ez_create_file_event(main_event, s, AE_READABLE, accept_handler, NULL);
	ez_create_file_event(main_event, s6, AE_READABLE, accept_handler, NULL);
	// 加入一个wgkwyt令命行控制功能.
	ez_create_file_event(main_event, fileno(stdin), AE_READABLE, read_char_from_stdin, NULL);
	{
		ez_create_time_event(main_event, 1000, time_out_handler, NULL);
		ez_create_time_event(main_event, 5000, time_out_handler, NULL);
	}

	ez_run_event_loop(main_event);

	log_info("delete event loop");
	ez_delete_event_loop(main_event);

	ez_net_close_socket(s);
	ez_net_close_socket(s6);

	log_release();
	return 0;
}
